﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Platform.LanguageProcessing.Expressions;

namespace Platform.LanguageProcessing
{
	/// <summary>
	/// Extrai todas as palavras e símbolos de um texto
	/// </summary>
	public sealed class Scanner
	{
		private int position;
		private string source;
		private int line;
		private TokenType cToken;
		private WordLocation cLoc;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="source"></param>
		internal Scanner( string source )
		{
			if ( source == null )
				throw new ArgumentNullException( "source" );

			// Garante que vai ter o sentila '.'
			this.source = ( source.EndsWith( "." ) ? source : String.Concat( source, "." ) );

			this.position = 0;
			this.line = 1;

			// Posiciona no primeiro token
			this.NextToken();
		}

		/// <summary>
		/// Obtém o token extraído atualmente
		/// </summary>
		public TokenType Token
		{
			get { return this.cToken; }
		}

		/// <summary>
		/// Obtém a palavra extraída atualmente
		/// </summary>
		public string Word
		{
			get { return this.source.Substring( this.cLoc.Start, this.cLoc.Length ); }
		}

		/// <summary>
		/// Obtém a localização da palavra ou símbolo extraído atualmente do texto
		/// </summary>
		public WordLocation Location
		{ get { return this.cLoc; } }

		/// <summary>
		/// Retorna true se chegou no final do texto 
		/// </summary>
		public bool EndOfText
		{
			get { return this.position >= this.source.Length; }
		}

		/// <summary>
		/// Move para o próximo token
		/// </summary>
		public void NextToken()
		{
			if ( this.EndOfText )
				return;

			// Ignora os espaços em branco
			while ( char.IsWhiteSpace( this.source[ this.position ] ) )
				this.NextChar();

			// Verifica qual é o token
			char cc = this.source[ this.position ];
			switch ( cc )
			{
				case '-':
					break;

				case '?':
					break;

				case '!':
					break;

				case ',':
					break;

				case '.':
					// Verifica se é '...'
					// this.SkipWhitespace();
					this.PeriodExpression();
					break;

				//case '\n':
				//    this.line++;
				//    break;

				default:
					if ( char.IsLetterOrDigit( cc ) )
					{
						this.WordExpression();
					}
					break;
			}
		}

		/// <summary>
		/// Extrai um palavra
		/// </summary>
		private void WordExpression()
		{
			this.cToken = TokenType.Word;
			int pos = this.position;

			while ( char.IsLetterOrDigit( this.source[ pos ] ) )
			{
				pos++;
			}
			this.MarkLocation( pos - this.position );
		}

		/// <summary>
		/// Extraí o final de uma frase
		/// </summary>
		private void PeriodExpression()
		{
			this.cToken = TokenType.Period;
			this.MarkLocation( 1 );
		}

		/// <summary>
		/// Extrai uma vírgula
		/// </summary>
		private void CommaExpression()
		{
			this.cToken = TokenType.Comma;
			this.MarkLocation( 1 );
		}

		/// <summary>
		/// Move o cursor para o próximo caracter do texto
		/// </summary>
		private void NextChar()
		{
			this.position++;
		}

		/// <summary>
		/// Move o cursor da posição atual até encontrar uma caracter que não é espaço em branco
		/// </summary>
		private void SkipWhitespace()
		{
			char cc = this.source[ this.position ];

			while ( char.IsWhiteSpace( cc ) )
			{
				if ( cc == '\n' )
					this.line++;

				this.NextChar();
				cc = this.source[ this.position ];
			}
		}

		private void MarkLocation( int len )
		{
			this.cLoc = new WordLocation( this.position, len, this.line );
			this.position += len;
		}
	}

}
